# jsb/plugs/gozer/infoitem.py
#
#

""" information items .. keyword/description pairs """

## jsb imports

from jsb.lib.commands import cmnds
from jsb.lib.examples import examples
from jsb.lib.datadir import datadir
from jsb.lib.persist import Persist
from jsb.utils.locking import lockdec
from jsb.utils.exception import handle_exception
from jsb.lib.callbacks import last_callbacks
from jsb.lib.users import users
from jsb.lib.config import getmainconfig

## basic imports

import thread
import os
import time
import logging

## alchemy imports

from jsb.database.alchemy import Base, create_session, trans, create_all, query
from datetime import datetime
from time import localtime
from sqlalchemy import Column, String, Integer, Text, DateTime, ForeignKey, Sequence
import sqlalchemy as sa

# locks

infolock = thread.allocate_lock()
locked = lockdec(infolock)

## Infoitems model

class InfoItems(Base):
    __tablename__ = 'infoitems'
    __table_args__ = {'useexisting': True}
    indx = Column('indx', Integer, primary_key=True)
    item = Column('item', String(255), nullable=False)
    description = Column('description', Text, nullable=False)
    userhost = Column('userhost', String(255), nullable=False)
    time = Column('time', DateTime, nullable=False)

    def __init__(self, item, description, userhost, ttime=None):
        self.time = ttime and ttime or datetime.now()
        self.item = item.lower()
        self.description = description
        self.userhost = userhost

# InfotitemsDb class

class InfoitemsDb(object):

    """ information items """

    @trans 
    def add(self, session, item, description, userhost, ttime, channel="", name=""):
        """ add an item """
        item = item.lower()
        result = 0
        try:
            newitem = InfoItems(item, description, userhost, datetime.fromtimestamp(ttime))
            session.add(newitem)
            session.commit()
            result = 1
        except Exception, ex: logging.warn("can't add item: %s" % str(ex)) ; raise ex
        return result

    def get(self, item):
        """ get infoitems """
        item = item.lower()
        result = [r.description for r in query(InfoItems).filter_by(item=item)]
        return result

    @trans
    def delete(self, session, indexnr):
        """ delete item with indexnr  """
        item = session.query(InfoItems).filter_by(indx=indexnr).first()
        try:
            session.delete(item)
            session.commit()
            result = 1
        except Exception, ex: logging.warn("can't delete item %s: %s" % (str(indexnr), str(ex))) ; raise ex
        return result

    @trans
    def deltxt(self, session, item, txt):
        """ delete item with matching txt """
        items = session.query(InfoItems).filter(InfoItems.item==item).filter(InfoItems.description.like('%%%s%%' % txt)).all()
        result = 0
        try:
            for n, i in enumerate(items): session.delete(i) ; result = n + 1
            session.commit()
        except Exception, ex: logging.warn("can't delete items like %s: %s" % (str(txt), str(ex))) ; raise ex
        return result

    def size(self):
        """ return number of items """
        count = query(sa.func.count(InfoItems.indx)).first()[0]
        return count

    def searchitem(self, search):
        """ search items """
        result = query(InfoItems).filter(InfoItems.item.like('%%%s%%' % search)).all()
        return result

    def searchdescr(self, search):
        """ search descriptions """
        result = query(InfoItems).filter(InfoItems.description.like('%%%s%%' % search)).all()
        return result


info = InfoitemsDb()
create_all('infoitem')
assert(info)

def size():
    """ return number of infoitems """
    return info.size()

def search(what, queue):
    logging.warn('searched for %s' % what)
    result = info.searchitem(what)   
    if not result: return
    res = []
    for i in result: queue.put_nowait(i.description)
    result = info.searchdescr(what)   
    if not result: return
    for i in result: queue.put_nowait("[%s] %s" % (i.item, i.description))


def handle_infosize(bot, ievent):
    """ info-size .. show number of information items """
    ievent.reply("we have %s infoitems" % info.size())

cmnds.add('info-size', handle_infosize, ['USER', 'WEB', 'CLOUD'])
examples.add('info-size', 'show number of infoitems', 'info-size')

def handle_addinfoitem(bot, ievent):
    """ <keyword> = <description> .. add information item """
    try: (what, description) = ievent.groups
    except ValueError: ievent.reply('i need <item> <description>') ; return
    if len(description) < 3:
        ievent.reply('i need at least 3 chars for the description')
        return
    what = what.strip()
    ret = info.add(what, description, ievent.userhost, time.time())
    if ret: ievent.reply('item added')
    else: ievent.reply('unable to add item')

cmnds.add('^(.+?)\s+=\s+(.+)$', handle_addinfoitem, ['USER', 'INFOADD'], how="regex", allowqueue=False)
examples.add('=', 'add description to item', 'dunk = top')

def handle_question(bot, ievent):
    """ <keyword>? .. ask for information item description """
    try: what = ievent.groups[0]
    except TypeError: what = ievent.rest
    except IndexError: ievent.reply('i need a argument') ; return
    what = what.strip().lower()
    infoitems = info.get(what)
    if infoitems: ievent.reply("%s is " % what, infoitems)
    else: ievent.reply('nothing known about %s' % what)

cmnds.add('^(.+)\?$', handle_question, ['USER', 'WEB', 'JCOLL', 'CLOUD'], how="regex", allowqueue=True)
cmnds.add('^\?(.+)$', handle_question, ['USER', 'WEB', 'JCOLL', 'CLOUD'], how="regex", allowqueue=True)
cmnds.add('?', handle_question, ['USER', 'INFOADD'], allowqueue=False)
examples.add('?', 'show infoitems of <what>', '1) test? 2) ?test')

def handle_forget(bot, ievent):
    """ forget <keyword> <txttomatch> .. remove information item where description matches txt given """
    if len(ievent.args) > 1: what = ' '.join(ievent.args[:-1]) ; txt = ievent.args[-1]
    else: ievent.missing('<item> <txttomatch> (min 3 chars)') ; return
    if len(txt) < 3: ievent.reply('i need txt with at least 3 characters') ; return
    what = what.strip().lower()
    try: nrtimes = info.deltxt(what, txt)
    except KeyError: ievent.reply('no records matching %s found' % what) ; return
    if nrtimes > 1: ievent.reply('%d items deleted' % nrtimes)
    elif nrtimes: ievent.reply('item deleted')
    else: ievent.reply('delete %s of %s failed' % (txt, what))

cmnds.add('info-forget', handle_forget, ['FORGET', 'OPER'])
examples.add('info-forget', 'forget <item> containing <txt>', 'info-forget dunk bla')

def handle_searchdescr(bot, ievent):
    """ info-sd <txttosearchfor> .. search information items descriptions """
    if not ievent.rest: ievent.missing('<txt>') ; return
    else: what = ievent.rest
    what = what.strip().lower()
    result = info.searchdescr(what)
    if result: 
        res = []
        for i in result: res.append("[%s] %s" % (i.item, i.description))
        ievent.reply("the following matches %s: " % what, res)
    else: ievent.reply('none found')

cmnds.add('info-sd', handle_searchdescr, ['USER', 'WEB', 'CLOUD'])
examples.add('info-sd', 'info-sd <txt> ..  search description of infoitems', 'info-sd http')

def handle_searchitem(bot, ievent):
    """ info-si <txt> .. search information keywords """
    if not ievent.rest: ievent.missing('<txt>') ; return
    else: what = ievent.rest
    what = what.strip().lower()
    result = info.searchitem(what)
    if result:
        res = []
        for i in result: res.append("[%s] %s" % (i.item, i.description))
        ievent.reply("the following matches %s: " % what, res)
    else: ievent.reply('none found')

cmnds.add('info-si', handle_searchitem, ['USER', 'WEB', 'CLOUD'])
examples.add('info-si', 'info-si <txt> ..  search the infoitems keys', \
'info-si test')
